﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Caching;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Profile;
using System.Web.Security;
using Lbb.User.Helpers;
using Lbb.User.Localization;

namespace Lbb.User
{
    /// <summary>
    /// 基于MembershipUser扩展的用户类
    /// </summary>
    /// <author>Chiron</author>
    public partial class MemberUser : MembershipUser, IDisposable
    {
        #region Fields
        public readonly static Regex UserNameReg = new Regex(@"^[0-9a-z\u4e00-\u9fa5]*$", RegexOptions.IgnoreCase | RegexOptions.Compiled);

        protected ProfileBase profile;

        private IDictionary<string, object> expandAttributes;

        protected const string NICK_NAME = "NickName";
        protected const string PORTRAIT = "Portrait";
        protected const string GENDER = "Gender";
        protected const string EXPAND_ATTRIBUTE = "ExpandAttribute";

        private readonly static PropertyInfo[] membershipUserProperties = typeof(MembershipUser).GetProperties();
        private readonly static DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(IDictionary<string, object>));

        #endregion Fields

        #region Ctors

        /// <summary>
        /// 以当前登录的用户构建.
        /// </summary>
        public MemberUser()
        {
            var mUser = Membership.GetUser();
            if (mUser == null) throw new Exception(MemberUserRes.NotLogined);
            Init(mUser);
        }
        /// <summary>
        /// 从指定的 MembershipUser 中构建.
        /// </summary>
        /// <param name="membershipUser"></param>
        public MemberUser(MembershipUser membershipUser)
        {
            Init(membershipUser);
        }

        /// <summary>
        /// 从指定的账号中构建.
        /// </summary>
        /// <param name="userName">账号</param>
        /// <param name="userIsOnline">设置为在线状态</param>
        public MemberUser(string userName, bool userIsOnline = false)
        {
            if (string.IsNullOrEmpty(userName)) throw new ArgumentNullException("userName");
            var mUser = Membership.GetUser(userName, userIsOnline);
            if (mUser == null) throw new ArgumentException(string.Format(MemberUserRes.NotFindAccount, userName), "userName");
            Init(mUser);
        }
        /// <summary>
        /// 从指定的账号中构建.
        /// </summary>
        /// <param name="userId">用户ID</param>
        /// <param name="userIsOnline">设置为在线状态</param>
        public MemberUser(Guid userId, bool userIsOnline = false)
        {
            var mUser = Membership.GetUser(userId, userIsOnline);
            if (mUser == null) throw new ArgumentException(string.Format(MemberUserRes.NotFindUserId, userId), "userId");
            Init(mUser);
        }
        #endregion Ctors

        #region Methods

        /// <summary>
        /// 更改用户名(账号)
        /// </summary>
        /// <param name="newUserName">新用户名</param>
        /// <returns></returns>
        public MemberUser ChangeUserName(string newUserName)
        {
            if (string.IsNullOrEmpty(newUserName)) throw new ArgumentNullException("newUserName");
            if (!UserNameReg.IsMatch(newUserName)) throw new ArgumentException(AccountValidation.ErrorCodeToString(MembershipCreateStatus.InvalidUserName));

            if (newUserName.Equals(UserName, StringComparison.CurrentCultureIgnoreCase)) return this;
            if (ChangingUsername != null)
            {
                var eventArgs = new UserEventArgs { User = this };
                ChangingUsername.Invoke(this, eventArgs);
                if (eventArgs.Cancel) return this;
            }

            using (var repository = RepositroyHelper.GetUserRepository())
            {
                if (repository.Get(newUserName) != null) throw new ArgumentException(string.Format(MemberUserRes.UserNameUsed, newUserName));

                var aUser = repository.Get(UserName);
                if (aUser == null) throw new ArgumentException(MemberUserRes.NotFindCurrUser);
                aUser.UserName = newUserName;
                aUser.LoweredUserName = newUserName.ToLower();
                repository.Save();
            }

            var user = Membership.GetUser(newUserName);
            var mUser = new MemberUser(user);
            if (ChangedUsername != null) ChangedUsername.Invoke(this, new UserEventArgs { User = mUser });
            return mUser;
        }

        /// <summary>
        /// 取得头像
        /// </summary>
        /// <param name="size">大小</param>
        /// <returns></returns>
        public string GetAvatar(AvatarSize size = AvatarSize.Medium)
        {
            var result = string.Format(Portrait, size);
            if (string.IsNullOrEmpty(result))
            {
                result = UserConfig.DefPortrait;
                result = VirtualPathUtility.ToAbsolute(result);
            }
            return result;
        }

        /// <summary>
        /// 获得用于显示的用户名称
        /// </summary>
        /// <returns></returns>
        public string GetDisplayName()
        {
            if (string.IsNullOrEmpty(NickName))
            {
                return UserName;
            }
            else
            {
                return NickName;
            }
        }

        /// <summary>
        /// 获取欢迎信息
        /// </summary>
        /// <returns></returns>
        public string GetWelcomeMessage(string displayName = null)
        {
            if (string.IsNullOrEmpty(displayName)) displayName = GetDisplayName();
            var now = DateTime.Now;
            var welcomeMsg = string.Empty;
            var hour = now.TimeOfDay.TotalHours;
            if (hour < 6)
            {//凌晨
                welcomeMsg = string.Format(MemberUserRes.WelcomeMessageBeforeDawn, displayName, (int)hour);
            }
            else if (hour < 8)
            {//早晨
                welcomeMsg = string.Format(MemberUserRes.WelcomeMessageMorning, displayName);
            }
            else if (hour < 11)
            {//早上
                welcomeMsg = string.Format(MemberUserRes.WelcomeMessageForenoon, displayName);
            }
            else if (hour < 14)
            {//中午
                welcomeMsg = string.Format(MemberUserRes.WelcomeMessageNoon, displayName);
            }
            else if (hour < 18)
            {//下午
                welcomeMsg = string.Format(MemberUserRes.WelcomeMessageAfternoon, displayName);
            }
            else if (hour < 23)
            {//晚上
                welcomeMsg = string.Format(MemberUserRes.WelcomeMessageNight, displayName);
            }
            else
            {//深夜
                welcomeMsg = string.Format(MemberUserRes.WelcomeMessageLateAtNight, displayName);
            }
            return welcomeMsg;
        }

        /// <summary>
        /// 保存当前更改
        /// </summary>
        public void Save()
        {
            Membership.UpdateUser(this);
            profile.Save();
            RemoveCache(this);
        }

        /// <summary>
        /// 索引器
        /// </summary>
        /// <param name="attrName">属性名</param>
        /// <returns></returns>
        public object this[string attrName]
        {
            get
            {
                object rv = null;
                if (attrName == NICK_NAME || attrName == PORTRAIT || attrName == GENDER)
                {
                    rv = profile[attrName];
                }
                if (rv == null && ExpandAttributes.ContainsKey(attrName)) rv = ExpandAttributes[attrName];
                return rv;
            }
            set
            {
                if (attrName == NICK_NAME || attrName == PORTRAIT || attrName == GENDER)
                {
                    if (value is string)
                    {
                        profile[attrName] = value;
                    }
                    else
                    {
                        throw new ArgumentException(MemberUserRes.InvalidDataType);
                    }
                }
                else
                {
                    var extAttrs = ExpandAttributes;
                    extAttrs[attrName] = value;
                    ExpandAttributes = extAttrs;
                }
            }
        }

        /// <summary>
        /// 取得用户与指定程序有关的属性.
        /// </summary>
        /// <param name="appCode">程序编码</param>
        /// <returns></returns>
        public Dictionary<string, object> GetProperties(string appCode)
        {
            if (string.IsNullOrEmpty(appCode)) throw new ArgumentNullException("appCode");
            var result = new Dictionary<string, object>();
            var preKey = appCode + "_";
            foreach (var p in ExpandAttributes.Where(d => d.Key != preKey && d.Key.StartsWith(preKey)))
            {
                result.Add(p.Key.Remove(0, preKey.Length), p.Value);
            }

            return result;
        }

        /// <summary>
        /// 设置用户与指定程序有关的属性.
        /// </summary>
        /// <param name="appCode">程序编码</param>
        /// <param name="props">属性</param>
        public void SetProperties(string appCode, IDictionary<string, object> props)
        {
            if (string.IsNullOrEmpty(appCode)) throw new ArgumentNullException("appCode");
            if (props == null) throw new ArgumentNullException("props");
            var preKey = appCode + "_";
            if (props.Count > 0)
            {
                var attrs = ExpandAttributes;
                foreach (var p in props)
                {
                    var key = preKey + p.Key;
                    if (attrs.ContainsKey(key))
                    {
                        attrs[key] = p.Value;
                    }
                    else
                    {
                        attrs.Add(key, p.Value);
                    }
                }
                ExpandAttributes = attrs;
            }
        }

        #region Helpers
        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="membershipUser"></param>
        protected void Init(MembershipUser membershipUser)
        {
            if (membershipUser == null) throw new ArgumentNullException("membershipUser");
            foreach (var pif in membershipUserProperties)
            {
                if (pif.Name != "IsOnline")
                {
                    var v = pif.GetValue(membershipUser, null);
                    try
                    {
                        pif.SetValue(this, v, null);
                    }
                    catch (ArgumentException)
                    {
                        var fi = typeof(MembershipUser).GetField("_" + pif.Name, BindingFlags.NonPublic | BindingFlags.Instance);
                        if (fi == null) throw new ArgumentException(MemberUserRes.UnableToSetPropertie, pif.Name);
                        fi.SetValue(this, v);
                    }
                    catch (Exception)
                    {
                        throw;
                    }
                }
            }
            profile = DefaultProfile.Create(UserName);
            BindEvent(this);

            Cache.Add(UserName.ToLower(), this, ObjectCache.InfiniteAbsoluteExpiration);
            Cache.Add(ProviderUserKey.ToString(), this, ObjectCache.InfiniteAbsoluteExpiration);
        }

        #endregion Helpers

        #endregion Methods

        #region Props
        /// <summary>
        /// 昵称
        /// </summary>
        public string NickName
        {
            get
            {
                return profile[NICK_NAME] as string;
            }
            set
            {
                profile[NICK_NAME] = value;
            }
        }

        /// <summary>
        /// 头像
        /// </summary>
        public string Portrait
        {
            get
            {
                return profile[PORTRAIT] as string;
            }
            set
            {
                profile[PORTRAIT] = value;
            }
        }

        /// <summary>
        /// 性别
        /// </summary>
        public string Gender
        {
            get
            {
                return profile[GENDER] as string;
            }
            set
            {
                profile[GENDER] = value;
            }
        }

        /// <summary>
        /// 获取或设置拓展属性
        /// </summary>
        public IDictionary<string, object> ExpandAttributes
        {
            get
            {
                if (expandAttributes == null)
                {
                    try
                    {
                        using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(profile[EXPAND_ATTRIBUTE] as string)))
                        {
                            expandAttributes = serializer.ReadObject(ms) as IDictionary<string, object>;
                        }
                    }
                    catch (Exception ex)
                    {
                        expandAttributes = new Dictionary<string, object>();
                    }
                }
                return expandAttributes;
            }

            set
            {
                expandAttributes = value;
                if (expandAttributes == null || expandAttributes.Count < 1)
                {
                    profile[EXPAND_ATTRIBUTE] = string.Empty;
                }
                else
                {
                    using (var sr = new StreamReader(new MemoryStream()))
                    {
                        serializer.WriteObject(sr.BaseStream, expandAttributes);
                        sr.BaseStream.Position = 0;
                        profile[EXPAND_ATTRIBUTE] = sr.ReadToEnd();
                    }
                }
            }
        }
        #endregion Props

        #region Events
        /// <summary>
        /// 用户名更改事件
        /// </summary>
        internal event EventHandler<UserEventArgs> OnChangingUsername;
        /// <summary>
        /// 完成用户名更改事件
        /// </summary>
        internal event EventHandler<UserEventArgs> OnChangedUsername;

        #endregion Events

        public void Dispose()
        {
            UnBindEvent(this);
            RemoveCache(this);
        }
    }

    #region ViewModel
    public class KeyAndValue
    {
        public string Key { get; set; }
        public object Value { get; set; }
    }

    /// <summary>
    /// MemberUser视图模型
    /// </summary>
    public class MemberUserModel
    {
        public MemberUserModel()
        {
        }

        public MemberUserModel(MemberUser mUser)
        {
            if (mUser == null) throw new ArgumentNullException("mUser");
            UserName = mUser.UserName;
            Portrait = mUser.Portrait;
            NickName = mUser.NickName;
            Gender = mUser.Gender;
            LastLoginDate = mUser.LastLoginDate;
            IsOnline = mUser.IsOnline;
            IsLockedOut = mUser.IsLockedOut;
            IsApproved = mUser.IsApproved;
            Email = mUser.Email;
            Comment = mUser.Comment;
            CreationDate = mUser.CreationDate;
            ExpandAttributes = new List<KeyAndValue>();
            foreach (var extAttr in mUser.ExpandAttributes)
            {
                ExpandAttributes.Add(new KeyAndValue { Key = extAttr.Key, Value = extAttr.Value });
            }
        }

        public List<KeyAndValue> ExpandAttributes { get; set; }

        public string UserName { get; set; }

        public string Portrait { get; set; }

        public string NickName { get; set; }

        public string Gender { get; set; }

        public DateTime LastLoginDate { get; set; }

        public bool IsOnline { get; set; }

        public bool IsLockedOut { get; set; }

        public bool IsApproved { get; set; }

        public string Email { get; set; }

        public string Comment { get; set; }

        public DateTime CreationDate { get; set; }

    }
    #endregion ViewModel

}